package com.cms.juc;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;

public class NotSafeDemo03 {
    public static void main(String[] args) {
        // setNodeSafe();
        // hashMapSafe();
        // Collection
    }

    private static void hashMapSafe() {
        /*
         * map集合的多线程使用
         * java.util.ConcurrentModificationException
         * 解决：
         * JUC ConcurrentHashMap 只是在put中的加上了synchronized重锁，
         */
        Map<String, String> map = new ConcurrentHashMap<>();
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 8));
                System.out.println(map);
            }, String.valueOf(i)).start();
        }
    }

    private static void setNodeSafe() {
        // extracted();
        // set集合的多线程使用
        /* HashSet 底层是 HashMap
         * java.util.ConcurrentModificationException
         * 解决方法和ArrayList一样，使用JUC包里面的 CopyOnWriteArraySet
         * 1. CopyOnWriteArraySet继承于AbstractSet，这就意味着它是一个集合。
          2. CopyOnWriteArraySet包含CopyOnWriteArrayList对象，它是通过CopyOnWriteArrayList实现的。而CopyOnWriteArrayList本质是个动态数组队列，
        所以CopyOnWriteArraySet相当于通过通过动态数组实现的“集合”！ CopyOnWriteArrayList中允许有重复的元素；但是，CopyOnWriteArraySet是一个集合
        * ，所以它不能有重复集合。因此，CopyOnWriteArrayList额外提供了addIfAbsent()和addAllAbsent()这两个添加元素的API，
        * 通过这些API来添加元素时，只有当元素不存在时才执行添加操作！
         */
        Set<String> set = new CopyOnWriteArraySet<>();//new HashSet<>();
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                /*
                 * set.add 确实是使用的map的put
                 * map.put(e, PRESENT)==null;
                 * e:元素
                 * PRESENT = new Object(); 是一个object常量
                 * 只存 K，V随意，所以V就存储一个object的常量就可以
                 */
                /*public V put(K key, V value) {
                    // 倒数第二个参数false：表示允许旧值替换
                    // 最后一个参数true：表示HashMap不处于创建模式
                    return putVal(hash(key), key, value, false(表示允许旧值替换), true(表示HashMap不处于创建模式));
                }*/
                set.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(set);
            }, String.valueOf(i)).start();
        }
    }

    private static void extracted() {
        /*  ArrayList底层是数组
         *  Collection 是 List的父类 是接口
         *  Collections 是 集合接口的工具类
         */
        // jdk1.8 默认容量是0
        // 加元素容量是10 ，每次扩容是原始容量的二分之一，并进行取整操作
        // CopyOnWriteArrayList 写时复制类
        List<String> list = new CopyOnWriteArrayList<>(); //Collections.synchronizedList(new ArrayList<>()); // new Vector<>();// new ArrayList<>();
        // set map 线程不安全

//        list.add("a");
//        list.add("a");
//        list.add("a");
//        list.forEach(System.out::println);
        /*
         * ArrayList<>() 线程不安全。多线程下，会造成数据重复，或者数据没有加进去的异常，效率第一
         * error：java.util.concurrentModificationException
         * 不能说自己重新新一个add加上锁 synchronized
         * 解决：
         * 3.1 Vector<>() 的add自带锁 synchronized
         *     Vector 是线程安全的，因为加上重锁了，性能差 不能用
         * 3.2 Collections.synchronizedList(new ArrayList<>()) 也不能用 用java里面的并发包
         * 3.3 CopyOnWriteArrayList 写时复制类
         *     ReentrantLock 里面使用的重入锁
         *
         */
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(list);
            }, String.valueOf(i)
            ).start();
        }
    }
}
/*
 * 笔记
 * 写时复制
 * CopyOnWrite 容器即写时复制的容器，往一个容器添加元素的时候，不直接往当前容器Object[]添加，而是先将当前容器Object[]进行复制，
 * 复制出一个新的Object[] newElements 然后再在新的容器Object[] newElements中添加元素，添加完成元素后，再将原容器的指引指向新的容器，
 * 好处是进行并发读的时候，不需要加上synchronized重锁，因为当前容器不会添加任何元素，是一种读写分离的思想，读写都是不同的容器。
 *
 * public boolean add(E e) {
        * 定义重入锁
        final ReentrantLock lock = this.lock;
        * 加锁
        lock.lock();
        try {
            * 获取当前容器
            Object[] elements = getArray();
            * 当前容器中元素的个数
            int len = elements.length;
            * 复制原来的容器并进行扩容（扩容是1）
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            * 赋值新的元素位置
            newElements[len] = e;
            * 改变原来容器
            setArray(newElements);
            * 修改完成
            return true;
        } finally {
            * 解锁
            lock.unlock();
        }
    }
 *
 */
